package org.example.auth.service.impl;

import org.example.auth.config.Constant;
import org.example.auth.contract.request.AuthorityRequestForRoleBind;
import org.example.auth.enums.AuthExceptionEnum;
import org.example.auth.model.entity.Authority;
import org.example.auth.model.entity.RoleAuthority;
import org.example.auth.repository.AuthorityRepository;
import org.example.auth.repository.RoleAuthorityRepository;
import org.example.auth.service.RoleAuthorityService;
import org.example.misc.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class RoleAuthorityServiceImpl implements RoleAuthorityService {
    @Autowired
    private RoleAuthorityRepository roleAuthorityRepository;
    @Autowired
    private AuthorityRepository authorityRepository;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Transactional(rollbackOn = Throwable.class)
    public void deleteRoleAuthorityByRoleId(Long roleId) {
        List<RoleAuthority> roleAuthorityList = roleAuthorityRepository.findByRoleId(roleId);
        if (CollectionUtils.isEmpty(roleAuthorityList)) {
            return;
        }
        roleAuthorityRepository.deleteAll(roleAuthorityList);
        String redisKey = Constant.REDIS_ROLE_ROUTE_PREFIX + roleId;
        Set<Object> members = redisTemplate.opsForSet().members(redisKey);
        if (CollectionUtils.isEmpty(members)) {
            return;
        }
        redisTemplate.opsForSet().remove(redisKey, members);
    }

    @Override
    public List<RoleAuthority> findByRoleId(Long roleId) {
        return roleAuthorityRepository.findByRoleId(roleId);
    }

    @Override
    @Transactional(rollbackOn = Throwable.class)
    public void deleteRoleAuthorityByAuthority(Authority authority) {
        List<RoleAuthority> roleAuthorityList = roleAuthorityRepository.findByAuthorityId(authority.getId());
        if (CollectionUtils.isEmpty(roleAuthorityList)) {
            return;
        }
        roleAuthorityRepository.deleteAll(roleAuthorityList);
        for (RoleAuthority roleAuthority : roleAuthorityList) {
            redisTemplate.opsForSet().remove(Constant.REDIS_ROLE_ROUTE_PREFIX + roleAuthority.getRoleId(), roleAuthority.getAuthorityId());
        }
    }

    /**
     * 单个角色批量操作权限
     */
    @Override
    @Transactional(rollbackOn = Throwable.class)
    public void batchBindRoleAuthority(AuthorityRequestForRoleBind request) {
        // 获取用户现在绑定权限
        List<RoleAuthority> roleAuthorityList = roleAuthorityRepository.findByRoleId(request.getRoleId());
        Set<Long> nowRoleAuthorityIdList = roleAuthorityList.stream().map(RoleAuthority::getAuthorityId).collect(Collectors.toSet());
        // 用户需要新增的绑定权限
        List<RoleAuthority> needBindAuthoritySet = new ArrayList<>();
        for (Long bindAuthorityId : request.getBindAuthorityIds()) {
            if (!nowRoleAuthorityIdList.contains(bindAuthorityId)) {
                RoleAuthority roleAuthority = new RoleAuthority();
                roleAuthority.setRoleId(request.getRoleId());
                roleAuthority.setAuthorityId(bindAuthorityId);
                needBindAuthoritySet.add(roleAuthority);
            }
        }
        roleAuthorityRepository.saveAll(needBindAuthoritySet);
        // 用户需要解绑的权限
        List<RoleAuthority> needUnbindAuthoritySet = new ArrayList<>();
        for (RoleAuthority roleAuthority : roleAuthorityList) {
            if (!request.getBindAuthorityIds().contains(roleAuthority.getAuthorityId())) {
                needUnbindAuthoritySet.add(roleAuthority);
                nowRoleAuthorityIdList.remove(roleAuthority.getAuthorityId());
            }
        }
        roleAuthorityRepository.deleteAll(needUnbindAuthoritySet);
        // 刷新缓存
        List<Authority> needBindAuthorityList = authorityRepository.findByIdIn(nowRoleAuthorityIdList);
        Set<String> contentSet = needBindAuthorityList.stream().map(Authority::getContent).collect(Collectors.toSet());
        String redisKey = Constant.REDIS_ROLE_ROUTE_PREFIX + request.getRoleId();
        Set<Object> members = redisTemplate.opsForSet().members(redisKey);
        redisTemplate.opsForSet().remove(redisKey, members);
        redisTemplate.opsForSet().add(redisKey, contentSet);
    }

    /**
     * 单个角色绑定权限
     */
    @Override
    @Transactional(rollbackOn = Throwable.class)
    public void bindRoleAuthority(Long roleId, Long authorityId) {
        Optional<RoleAuthority> optional = roleAuthorityRepository.findByRoleIdAndAuthorityId(roleId, authorityId);
        if (optional.isEmpty()) {
            RoleAuthority roleAuthority = new RoleAuthority();
            roleAuthority.setRoleId(roleId);
            roleAuthority.setAuthorityId(authorityId);
            roleAuthorityRepository.save(roleAuthority);
        }
        Authority authority = authorityRepository.findById(authorityId)
                .orElseThrow(() -> new ServiceException(AuthExceptionEnum.AUTHORITY_NOT_EXISTED));
        redisTemplate.opsForSet().add(Constant.REDIS_ROLE_ROUTE_PREFIX + roleId, authority.getContent());
    }

    /**
     * 单个角色解绑权限
     */
    @Override
    @Transactional(rollbackOn = Throwable.class)
    public void unBindRoleAuthority(Long roleId, Long authorityId) {
        Authority authority = authorityRepository.findById(authorityId)
                .orElseThrow(() -> new ServiceException(AuthExceptionEnum.AUTHORITY_NOT_EXISTED));
        Optional<RoleAuthority> optional = roleAuthorityRepository.findByRoleIdAndAuthorityId(roleId, authorityId);
        optional.ifPresent(roleAuthority -> roleAuthorityRepository.delete(roleAuthority));
        redisTemplate.opsForSet().remove(Constant.REDIS_ROLE_ROUTE_PREFIX + roleId, authority.getContent());
    }
}
